/*
 * Copyright (c) [2005] [Jeffrey Moore]
 *
 * Redistributions in source code form must reproduce the above copyright and 
 * this condition.
 *
 * The contents of this file are subject to the Sun Project JXTA License 
 * Version 1.1 (the "License"); you may not use this file except in compliance 
 * with the License. A copy of the License is available at 
 * http://www.jxta.org/jxta_license.html.
 *
 */


/*
 * LocalMonitorControl.java
 *
 * Created on April 7, 2005, 9:14 AM
 */

package net.jxta.myjxta.plugins.vijxta;


import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoDataSourceException;
import javax.media.control.FormatControl;
import javax.media.control.MonitorControl;
import javax.media.format.RGBFormat;
import javax.media.format.VideoFormat;
import javax.media.format.YUVFormat;
import javax.media.protocol.CaptureDevice;
import javax.media.protocol.DataSource;
import javax.swing.JLabel;
import java.util.logging.Level;
import net.jxta.logging.Logging;
import java.util.logging.Logger;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.endpoint.ByteArrayMessageElement;
import net.jxta.myjxta.dialog.DialogMessage;
/**
 * This class controls the hardware video capturedevice, it's formats and creating
 * the capture component for use in the UI. This class also dispatches messages
 * to the remote peer(s). Current implementation is to encode a frame as a jpeg
 * and send those bytes in a message accross the wire.
 * @author jamoore
 * @modified 2005-04-07 jamoore refactored
 */
public class LocalDeviceMonitorControl extends Thread implements ImageCaptureListener, Monitor, DeviceMonitorControl {
    
    static final Logger LOG = Logger.getLogger (LocalDeviceMonitorControl.class.getName());
    
    private static final String REFRESH_RATE_UNIT_LABEL = "Frames Per Second";
    
    private LinkedList outgoingBuffer = null;
    
    private ViJxtaDialog viJxtaDialog = null;
    
    private Hashtable deviceTable = null;
    
    private VideoFormat selectedVideoFormat  = null;
    
    private MonitorCDS monitorDataSource = null;
    
    private int refreshRate = this.DEFAULT_FRAMES_PER_SECOND;
    
    private long imageCaptureDelay = 0;
    
    private static final int DEFAULT_IMAGES_PER_MESSAGE = 1;
    
    private int imagesPerMessage = DEFAULT_IMAGES_PER_MESSAGE ;
    
    private MonitorControl monitorControl = null;
    
    private int imageCompression = Monitor.DEFAULT_IMAGE_COMPRESSION;
    
    private CaptureDeviceInfo selectedCaptureDeviceInfo = null;
    
    private MyLock captureLock = null;
    
    private MyLock monitorPausedLock = null;
    
    private MyLock transmitPausedLock = null;
    
    private String mimeType = null;
    
    private MyLock bufferStarvedLock = null;
    
    private JPEGImageWriteParam jpegParams = null;
    
    private ImageWriter imageWriter = null;
    
    private long imageEncodeTime = 0;
    
    private long averageImageEncodeTime = 0;
    
    private DialogMessage templateMessage = null;
    
    private int bytesSent = 0;
    
    private int messagesSent = 0;
    
    private int monitorState = STOPPED;
    
    private int transmitState = STOPPED;
    
    private boolean transmit = false;
    
    private Hashtable refreshRateTable = null;
    
    private Hashtable refreshRateLabelTable = null;
    
    private Thread dispatchThread = null;
    
    private Component monitor;
    
    
    public LocalDeviceMonitorControl (ViJxtaCallControl callControl) {
        LOG.setLevel (Level.INFO);
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("LocalMonitorControl Instantiated");
                    }
        
        refreshRateTable = new Hashtable();
        
        refreshRateTable.put(new Integer(1), new Long((long) this.SECOND / 1));
        refreshRateTable.put(new Integer(2), new Long((long) this.SECOND / 2));
        refreshRateTable.put(new Integer(3), new Long((long) this.SECOND / 2));
        refreshRateTable.put(new Integer(4), new Long((long) this.SECOND / 4));
        refreshRateTable.put(new Integer(5), new Long((long) this.SECOND / 5));
        refreshRateTable.put(new Integer(6), new Long((long) this.SECOND / 6));
        refreshRateTable.put(new Integer(7), new Long((long) this.SECOND / 7));
        refreshRateTable.put(new Integer(8), new Long((long) this.SECOND / 8));
        refreshRateTable.put(new Integer(9), new Long((long) this.SECOND / 9));
        refreshRateTable.put(new Integer(10), new Long((long) this.SECOND / 10));
        refreshRateTable.put(new Integer(11), new Long((long) this.SECOND / 11));
        refreshRateTable.put(new Integer(12), new Long((long) this.SECOND / 12));
        refreshRateTable.put(new Integer(13), new Long((long) this.SECOND / 13));
        refreshRateTable.put(new Integer(14), new Long((long) this.SECOND / 14));
        refreshRateTable.put(new Integer(15), new Long((long) this.SECOND / 15));
        
        refreshRateLabelTable = new Hashtable () ;
        
        refreshRateLabelTable.put (new Integer (1), new JLabel ("1"));
        refreshRateLabelTable.put (new Integer (3), new JLabel ("3"));
        refreshRateLabelTable.put (new Integer (6), new JLabel ("6"));
        refreshRateLabelTable.put (new Integer (9), new JLabel ("9"));
        refreshRateLabelTable.put (new Integer (11), new JLabel ("11"));
        refreshRateLabelTable.put (new Integer (13), new JLabel ("13"));
        refreshRateLabelTable.put (new Integer (15), new JLabel ("15"));
        
        this.imageCaptureDelay = ((Long)refreshRateTable.get(new Integer(this.DEFAULT_FRAMES_PER_SECOND))).longValue ();
        
        this.viJxtaDialog = (ViJxtaDialog) callControl.getDialog ();
        
        outgoingBuffer = new LinkedList ();
        
        captureLock = new MyLock ();
        
        captureLock.setLocked (true);
        
        monitorPausedLock = new MyLock ();
        
        transmitPausedLock = new MyLock ();
        
        bufferStarvedLock = new MyLock ();
        
        deviceTable = new Hashtable ();
        
        templateMessage = new DialogMessage (callControl.getDialog ().getGroup ()
        .getPeerGroup ().getPeerName (), null, callControl.getDialog ().getGroup ()
        .getPeerGroup ().getPeerGroupID ().toString (), callControl.getDialog ()
        .getGroup ().getPeerGroup ().getPeerGroupName ());
        
        /** consumer thread... takes from the queue and sends message*/
        dispatchThread = new Thread() {

            public void run() {
                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                    LOG.info("Transmit (Consumer) Thread: RUN");
                }
                while (true) {
                    if (getTransmitState() == Monitor.STOPPED) {
                        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                            LOG.info("run : Stopped");
                        }
                        // we should send any remaining data in buffer first
                        break;
                    }
                    if (getTransmitState() == Monitor.PAUSED) {
                        synchronized (transmitPausedLock) {
                            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                                LOG.info("run : tramsmit Paused");
                            }
                            try {
                                transmitPausedLock.wait();
                            }
                            catch (InterruptedException ix) {
                                ix.printStackTrace();
                            }
                        }
                    }
                    if (outgoingBuffer.size() >= getImagesPerMessage()) {
                        dispatchImage(getImagesPerMessage());
                    } else {
                        try {
                            synchronized (bufferStarvedLock) {
                                bufferStarvedLock.setLocked(true);
                                bufferStarvedLock.wait();
                            }
                        }
                        catch (InterruptedException ix) {
                            ix.printStackTrace();
                        }
                    }
                }
                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                    LOG.info("Transmit (Consumer) Thread: ENDED");
                }
            }
        };
    }
    
    public static boolean isJMFPresent() {
        LOG.setLevel(Level.INFO);
        
        boolean isPresent = true;
        try {
            
            VideoFormat f = new VideoFormat(VideoFormat.RGB);
            
        }catch(Exception x ) {
        
            x.printStackTrace ();
            isPresent = false;
        }catch(Error e) {
        
            e.printStackTrace ();
            isPresent = false;
        }
        
        return isPresent;
    }
    
    /** producer... runs every n (ms)((fps), sets the capture flag*/
    public void run () {
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("Image Capture (Producer) Thread RUN");
                    }
        while(true) {
            
            /** module state mechanics*/
            if(getMonitorState () == this.PAUSED) {
                
                synchronized(monitorPausedLock) {
                    if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("run : monitor Paused");
                    }
                    try {
                        
                    monitorPausedLock.wait ();
                    
                    }catch(InterruptedException ix) {
                        ix.printStackTrace ();
                    }
                }
            }
            
            if(getMonitorState () == this.STOPPED) {
                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                    LOG.info("run : Stopped");
                }
                
                break;
            }
            
            try {
                
                Thread.sleep (getImageCaptureDelay ());
                
            }catch(InterruptedException ix) {
                ix.printStackTrace ();
            }
            
            synchronized(captureLock) {
                
                captureLock.setLocked (false);
            }
        }
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("Image Capture (Producer) Thread Ended");
                    }
    }
    
    protected void dispatchImage (int imagesInThisMessage) {
        
        if(outgoingBuffer.size () >= imagesInThisMessage) {
            
            
            byte[] imageBytes = null;
            
            DialogMessage msg = getNewTemplateMessage ();
            
            StringMessageElement commandElement = new StringMessageElement (
                    ViJxtaCallControl.TAG_SESSION_COMMAND, ViJxtaCallControl.COMMAND_VIJXTA_DATA, null);
            
            msg.addMessageElement (ViJxtaCallControl.TAG_SESSION_COMMAND, commandElement);
            
            
            
                
                imageBytes = ((ByteArrayOutputStream)outgoingBuffer.getFirst ()).toByteArray ();
                
                outgoingBuffer.removeFirst();
                
            
               
            
            ByteArrayMessageElement imageElement = new ByteArrayMessageElement (
                    ViJxtaCallControl.TAG_IMAGE_DATA, null, imageBytes, 0, null);
            
            msg.addMessageElement (ViJxtaCallControl.TAG_IMAGE_DATA, imageElement);
            
            this.messagesSent = this.messagesSent + 1;
            
            this.bytesSent = this.bytesSent + imageBytes.length;
            
            this.viJxtaDialog.dispatch (msg);
            
        }
    }
    
    public String getRefreshRateUnitLabel() {
        
        return this.REFRESH_RATE_UNIT_LABEL;
    }
    
    public Hashtable getRefreshRateLabelTable() {
        
        return this.refreshRateLabelTable;
    }
    
    public Dimension getFormatSize() {
        
        
        return this.getSelectedVideoFormat ().getSize ();
    }
    
    public void resetMonitor() {
    
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("resetMonitor");
                    }    
        
            if(this.getTransmitState() == this.STOPPED) {
                
                this.stopMonitorCapture ();
                
                this.stopMonitor();
                
                this.releaseHardware ();
            }
        
    }
    
    public int getTransmitState() {
        
        return this.transmitState;
    }
    
    public void setTransmitState(int transmitState) {
        
        this.transmitState = transmitState;
    }
    
    
    public void pauseMonitor() {
        
        this.setMonitorState (this.PAUSED);
    }
    
    public void resumeMonitor() {
        
        this.setMonitorState (this.STARTED);
        
        synchronized(monitorPausedLock) {
            
            monitorPausedLock.notifyAll();
        }
    }
    
    public void pauseTransmit() {
        
        this.setTransmitState(this.PAUSED);
    }
    
    public void resumeTransmit() {
        
        this.setTransmitState (this.STARTED);
        
        synchronized(transmitPausedLock) {
            
            transmitPausedLock.notifyAll();
        }
    }
    
    public long getImageCaptureDelay () {
        
        return this.imageCaptureDelay;
    }
    
    public void setRefreshRate(int refreshRate) {
        
        if(refreshRate >= this.MINIMUM_FRAMES_PER_SECOND &&
                refreshRate <= this.MAXIMUM_FRAMES_PER_SECOND) {
            
            this.refreshRate = refreshRate;
            
            this.imageCaptureDelay = ((Long)refreshRateTable.get(new Integer(refreshRate))).longValue ();
        }else{
            
            this.refreshRate = this.DEFAULT_FRAMES_PER_SECOND;
            
            this.imageCaptureDelay = ((Long)refreshRateTable.get(new Integer(this.DEFAULT_FRAMES_PER_SECOND))).longValue ();
        }
        
    }
    
    public void addErrorListener(DeviceMonitorControl.DeviceErrorListener listener) {
        
    
    }
    
    public int getRefreshRate() {
        
        return this.refreshRate;
    }
    
    private void setupImageWriter () {
        
        Iterator writers = ImageIO.getImageWritersByFormatName (this.JPG_IMAGE_TYPE);
        
        if (writers.hasNext ()) {
            
            imageWriter = (ImageWriter)writers.next ();
        }else{
            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info("Writer not available for image format " +
                     this.JPG_MIME_TYPE);
            }
        }
        
        jpegParams = new JPEGImageWriteParam (null);
        
        jpegParams.setCompressionMode (ImageWriteParam.MODE_EXPLICIT);
        
        jpegParams.setCompressionQuality (this.getImageCompresionAsFloat (getImageCompression()));
    }
    
    private float getImageCompresionAsFloat(int intImageCompression) {
        
        float floatImageCompression = 0.0f;
        
        if(intImageCompression == 0) {
            
            floatImageCompression = 0.0f;
        }else{
            
            floatImageCompression = (float) ((float) intImageCompression / 100);
        }
        
        return floatImageCompression;
    }
    
    public void imageCaptured (Image image) {
        
        if(!captureLock.isLocked ()) {
            
            try {
                
                long in = System.currentTimeMillis ();
                
                BufferedImage capturedImage = new BufferedImage (
                        image.getWidth (null), image.getHeight (null),
                        BufferedImage.TYPE_INT_RGB);
                
                Graphics g = capturedImage.createGraphics ();
                
                g.drawImage (image, 0, 0, null);
                
                g.dispose ();
                
                ByteArrayOutputStream bos = new ByteArrayOutputStream ();
                
                ImageOutputStream ios = ImageIO.createImageOutputStream (bos);
                
                imageWriter.setOutput (ios);
                
                IIOImage iio = new IIOImage (capturedImage,null,null);
                
                imageWriter.write (null,iio,jpegParams);
                
                ios.flush ();
                
                ios.close ();
                
                outgoingBuffer.addLast (bos);
                
                long out = System.currentTimeMillis ();
                
                this.imageEncodeTime = out - in;
                
                if(this.averageImageEncodeTime != 0 ) {
                    
                    this.averageImageEncodeTime = 
                            (this.averageImageEncodeTime + this.imageEncodeTime) / 2;
                }
                
            }catch(Exception x) {
                x.printStackTrace ();
            }
            
            synchronized(captureLock) {
                
                captureLock.setLocked (true);
            }
            
            if(bufferStarvedLock.isLocked ()) {
                
                synchronized(bufferStarvedLock) {
                    
                    bufferStarvedLock.setLocked (false);
                    
                    bufferStarvedLock.notifyAll ();
                }
            }
        }
    }
    
    private Iterator getCaptureDevices (VideoFormat videoFormat) {
        
        Iterator itr = CaptureDeviceManager.getDeviceList (
                videoFormat).iterator ();
        
        return itr != null ? itr : Collections.EMPTY_LIST.iterator ();
    }
    
    // returns a list of CaptureDeviceInfo
    protected Iterator getImagingCaptureDevices () {
        
        Iterator itr = null;
        
        itr = getCaptureDevices (new VideoFormat (VideoFormat.RGB));
        
        if(itr.hasNext ()) { 
           
           
        }else{
            
            
            
            itr = getCaptureDevices (new VideoFormat (VideoFormat.YUV));
            
            if(itr.hasNext ()) {
                
                
            }else{
                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("NO YUV cap device");
                }
            }
        }
        
        return itr;
    }
    
    // returns a list of strings. capture device names.
    public Iterator getCaptureDeviceNames() {
        
        ArrayList deviceNames = new ArrayList();
        
        Iterator itr = getImagingCaptureDevices();
        
        
        
        while(itr.hasNext ()) {
            
            
            
            CaptureDeviceInfo deviceInfo = (CaptureDeviceInfo) itr.next ();
            
            deviceNames.add (deviceInfo.getName ());
        }
        
        return deviceNames.iterator ();
    }
    
    
    
    public void setCaptureDevice(String deviceName) {
        
        Iterator itr = getImagingCaptureDevices ();
            
            while(itr.hasNext()) {
                
                CaptureDeviceInfo cdi = (CaptureDeviceInfo) itr.next ();
                
                if(cdi.getName ().equals (deviceName)) {
                    
                    setCaptureDevice(cdi);
                    
                    break;
                }
            }
    }
    
    
    protected void setCaptureDevice (CaptureDeviceInfo captureDeviceInfo) {
        
        this.selectedCaptureDeviceInfo = captureDeviceInfo;
    }
    
    public CaptureDeviceInfo getSelectedCaptureDevice () {
        
        return this.selectedCaptureDeviceInfo;
    }
    
    protected Format[] getSelectedCaptureDeviceFormats () {
        
        CaptureDeviceInfo captureDeviceInfo = getSelectedCaptureDevice ();
        
        return captureDeviceInfo != null ? captureDeviceInfo.getFormats () : null;
    }
    
    // returns a string of format sizes
    public Iterator getCaptureDeviceFormats() {
        
        ArrayList formatStrings = new ArrayList();
        
        Format[] formats = getSelectedCaptureDevice().getFormats ();
        
        for (int i = 0; i < formats.length; i++) {
            String size = null;

            if (formats[i] instanceof RGBFormat) {
                RGBFormat rgbFormat = (RGBFormat) formats[i];

                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                    LOG.info("format.getSize " + rgbFormat.getSize());
                }
                if (rgbFormat.getSize() != null) {
                    size = ViJxtaUtil.dimensionToString(rgbFormat.getSize());
                    formatStrings.add(size);
                } else {
                    if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("Null RGB formatSize - discarded");
                    }
                }
            } else if (formats[i] instanceof YUVFormat) {
                YUVFormat yuvFormat = (YUVFormat) formats[i];

                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                    LOG.info("format.getSize " + yuvFormat.getSize());
                }
                if (yuvFormat.getSize() != null) {
                    size = ViJxtaUtil.dimensionToString(yuvFormat.getSize());
                    formatStrings.add(size);
                } else {
                    if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("Null YUV formatSize - discarded");
                    }
                }
            }
        }
        
        return formatStrings.iterator ();
    }
    
    
    public void setFormat(String formatString) {

        Dimension size = ViJxtaUtil.stringToDimension (formatString);
        
        Format[] formats = getSelectedCaptureDeviceFormats ();
        
        for(int i = 0; i < formats.length; i++) {
            
            if(formats[i] instanceof RGBFormat) {
                
                RGBFormat rgbFormat = (RGBFormat) formats[i];
                
                if(rgbFormat.getSize ().equals (size)) {
                    
                    setFormat (rgbFormat);
                    
                    break;
                }
                
                
            }else if(formats[i] instanceof YUVFormat) {
                
                YUVFormat yuvFormat = (YUVFormat) formats[i];
                
                if(yuvFormat.getSize ().equals (size)) {
                    
                    setFormat (yuvFormat);
                    
                    break;
                }
            }
        }
        
        setMimeType (Monitor.JPG_MIME_TYPE);
        
    }
    
    protected void setFormat (VideoFormat videoFormat) {
        
        this.selectedVideoFormat = videoFormat;
    }
    
    private void setMimeType(String mimeType) {
        
        if(mimeType!=null) {
            
        
        this.mimeType = mimeType;
        }else{
            this.mimeType = this.UNKNOWN_MIME_TYPE;
        }
        
    }
    
    public long getAverageImageEncodeTime() {
        
        return this.averageImageEncodeTime;
        
    }
    
    public String getMimeType() {
        
        return this.mimeType!=null ? this.mimeType : this.UNKNOWN_MIME_TYPE;
    }

    
    public MediaLocator getSelectedCaptureDeviceMediaLocator () {
        
        return getSelectedCaptureDevice () != null ?
            getSelectedCaptureDevice ().getLocator () : null;
    }
    
    public MonitorCDS getCustomDataSource () {
        
        if( getMonitorDataSource () == null) {
            
            DataSource captureDataSource = null;
            
            System.out.println ("Building MonitorDataSource");
            
            MediaLocator captureLocator = getSelectedCaptureDeviceMediaLocator ();
            
            if(captureLocator == null) {
                
                System.out.println ("MediaLocator for capture device is NULL");
                
                return null;
            }
            
            try{
                
                captureDataSource = Manager.createDataSource (captureLocator);
                
            }catch(NoDataSourceException ndsx) {
                
                ndsx.printStackTrace ();
            }catch(IOException iox) {
                
                iox.printStackTrace ();
            }
            if(captureDataSource == null) {
                
                System.out.println ("DataSource is NULL");
                
                return null;
            }
            
            try{
                
                captureDataSource.connect ();
                
            }catch(IOException iox) {
                
                iox.printStackTrace ();
            }
            
            if(captureDataSource instanceof CaptureDevice) {
                
                CaptureDevice captureDevice = (CaptureDevice) captureDataSource;
                
                FormatControl[] controls = captureDevice.getFormatControls ();
                
                for(int i = 0 ; i < controls.length; i++) {
                    
                    FormatControl formatContorls = (FormatControl) controls[i];
                    
                    Format[] formats = formatContorls.getSupportedFormats ();
                    
                    for(int j = 0; j < formats.length; j++) {
                        
                        if (formats[j].matches (getSelectedVideoFormat ())) {
                            
                            Format capFormat = formats[j].intersects (getSelectedVideoFormat ());
                            
                            formatContorls.setFormat (capFormat);
                            
                            break;
                        }
                    }
                }
                
                
                monitorDataSource = new MonitorCDS (captureDataSource);
                
            }else {
                System.out.println ("DataSource is NOT an instance of CaptureDevice");
                
                return null;
            }
        }
        return monitorDataSource;
    }
    
    public void obtainHardware() {
        
        try{
            
            MonitorCDS ds = this.getCustomDataSource ();
            
            MonitorStream ms = (MonitorStream) ds.getStreams ()[0];
            
            ms.addImageCaptureListener (this);
            
            monitorControl = (MonitorControl)ds.getControl ("jmfsample.MonitorStream");
             if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info("Monitor control = " + monitorControl);
             }
            
            if (monitorControl != null) {
                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("Monitor control component = " +
                         monitorControl.getControlComponent());
                }
                
                setMonitorComponent ( monitorControl.getControlComponent ());
                
                if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("Monitor control component = " +
                         this.getMonitorComponent());
                }
                
            }else{
                
            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("NO MONITOR CONTROL");
            }
            }
            
        }catch(Exception iox) {
            
            stopMonitor ();
            
            stopMonitorCapture();
            
            iox.printStackTrace ();
        }
        
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("obtainHardware");
                    }    
    }
    
    public void startMonitorCapture () {
        
        this.setupImageWriter();

        this.setMonitorState (this.STARTED);
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("startMonitorCapture");
                    }    
        
    }
    
    public void startMonitor() {
        
        monitorControl.setEnabled (true);
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("startMonitor");
                    }    
    }
    
    public void stopMonitor() {
        if(monitorControl != null) {
            
        monitorControl.setEnabled (false);
        }
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("stopMonitor");
                    }    
    }
    
    public void startTransmit() {
        
        this.setTransmitState(this.STARTED);
        
        this.start();
        
        this.dispatchThread.start();
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("startTransmit");
                    }    
        
    }
    
    public boolean isTransmit () {
        
        return transmit;
    }
    
    public void setTransmit (boolean trans) {
        
        transmit = trans;
    }
    
    public int getOutgoingBufferSize () {
        
        return outgoingBuffer.size ();
    }
    
    
    
    public int getMessagesSent () {
        
        return messagesSent;
    }
    
    public int getBytesSent () {
        
        return bytesSent;
    }
    
    
    public void releaseHardware() {
    
        this.stopMonitor();
        
        this.stopMonitorCapture ();
        
        try {
            
            if(this.getMonitorDataSource () != null) {
                
                this.getMonitorDataSource ().stop ();
                
                this.getMonitorDataSource ().disconnect ();
                
                this.monitorControl = null;
                
                this.monitorDataSource = null;
            }
            
        }catch(IOException iox) {
            iox.printStackTrace ();
        }
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("Release Hardware");
                    }    
    }
    
    public void stopMonitorCapture () {
    
        this.setMonitorState (this.STOPPED);
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("stopMonitorCapture");
                    }    
    }
    
    public void stopTransmit() {
        
        this.setTransmitState (this.STOPPED);
        
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                        LOG.info("stopTransmit");
                    }    
    }
    
    
    
    
    
    
    public int getBufferSize() {
        
        return this.outgoingBuffer.size();
    }
    
    public DialogMessage getNewTemplateMessage () {
        
        return (DialogMessage)this.templateMessage.clone ();
    }
    
    public int getImagesPerMessage () {
        
        return this.imagesPerMessage;
    }
    
    public void setImagesPerMessage (int imagesPerMessage) {
        
        if(imagesPerMessage < this.MINIMUM_IMAGES_PER_MESSAGE) {
            
            this.imagesPerMessage = this.MINIMUM_IMAGES_PER_MESSAGE;
        }else{
            
            this.imagesPerMessage = imagesPerMessage;
        }
    }
    
    /**
     *  Statistical Accessor. Returns the time delta of encoding an image
     */
    public long getImageEncodeTime () {
        
        return this.imageEncodeTime;
    }
    
    public void setImageCompression (int imageCompression) {
        
        if(imageCompression >= this.MINIMUM_IMAGE_COMPRESSION) {
            
            this.imageCompression = imageCompression;
        }else {
            this.imageCompression = this.MINIMUM_IMAGE_COMPRESSION;
        }
    }
    
    public int getImageCompression () {
        
        return imageCompression;
    }
    
    
    private int getMonitorState () {
        
        return this.monitorState;
    }
    
    private void setMonitorState (int monitorState) {
        
        this.monitorState = monitorState;
    }
    
    class MyLock extends Object {
        
        private boolean locked = false;
        
        public boolean isLocked () {
            return locked;
        }
        public void setLocked (boolean locked) {
            this.locked = locked;
        }
    }
    
    
    public Component getMonitorComponent () {
        
        return this.monitor;
    }
    
    private void  setMonitorComponent (Component monitorComponent) {
        
        this.monitor = monitorComponent;
    }
    
    
    public VideoFormat getSelectedVideoFormat () {
        
        return this.selectedVideoFormat;
    }
    
    public MonitorCDS getMonitorDataSource () {
        
        return this.monitorDataSource;
    }
    
    protected void setMonitorDataSource (MonitorCDS monitorDataSource) {
        
        this.monitorDataSource = monitorDataSource;
    }
}
